use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use std::thread::Thread;

use crate::get_val_from_vm_val_enum;
use crate::heap::vm_heap::VMHeap;
use crate::instruction::instruction::Instruction;
use crate::vm_thread::{VMFrame, VMThread};
use crate::vm_val::{VMValEnum, VMValLong};

pub struct lload_0 {}

impl Instruction for lload_0 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 0_u8;
        let val = frame.borrow().get_long_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValLong {
            val
        })
    }

    fn to_string(&self) -> String {
        String::from("lload_0")
    }
}

pub struct lload_1 {}

impl Instruction for lload_1 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 1_u8;
        let val = frame.borrow().get_long_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValLong {
            val
        })
    }

    fn to_string(&self) -> String {
        String::from("lload_1")
    }
}


pub struct lload_2 {}

impl Instruction for lload_2 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 2_u8;
        let val = frame.borrow().get_long_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValLong {
            val
        })
    }

    fn to_string(&self) -> String {
        String::from("lload_2")
    }
}


pub struct lload_3 {}

impl Instruction for lload_3 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 3_u8;
        let val = frame.borrow().get_long_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValLong {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("lload_3")
    }
}


pub struct iload_0 {}

impl Instruction for iload_0 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 0_u8;
        let val = frame.borrow().get_int_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValInteger {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("iload_0")
    }
}

pub struct iload_1 {}

impl Instruction for iload_1 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 1_u8;
        let val = frame.borrow().get_int_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValInteger {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("iload_1")
    }
}


pub struct iload_2 {}

impl Instruction for iload_2 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 2_u8;
        let val = frame.borrow().get_int_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValInteger {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("iload_2")
    }
}


pub struct iload_3 {}

impl Instruction for iload_3 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 3_u8;
        let val = frame.borrow().get_int_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValInteger {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("iload_3")
    }
}


pub struct aload_0 {}

impl Instruction for aload_0 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 0_u8;
        let val = frame.borrow().get_ref_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValRef {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("aload_0")
    }
}

pub struct aload_1 {}

impl Instruction for aload_1 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 1_u8;
        let val = frame.borrow().get_ref_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValRef {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("aload_1")
    }
}


pub struct aload_2 {}

impl Instruction for aload_2 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 2_u8;
        let val = frame.borrow().get_ref_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValRef {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("aload_2")
    }
}


pub struct aload_3 {}

impl Instruction for aload_3 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 3_u8;
        let val = frame.borrow().get_ref_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValRef {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("aload_3")
    }
}


pub struct aload {
    pub index: u8,
}

impl Instruction for aload {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.index = frame.borrow_mut().read_u8();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let val = frame.borrow().get_ref_from_local_var(self.index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValRef {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("aload")
    }
}


pub struct dload_0 {}

impl Instruction for dload_0 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 0_u8;
        let val = frame.borrow().get_double_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValDouble {
            val
        })
    }

    fn to_string(&self) -> String {
        String::from("dload_0")
    }
}

pub struct dload_1 {}

impl Instruction for dload_1 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 1_u8;
        let val = frame.borrow().get_double_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValDouble {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("dload_1")
    }
}


pub struct dload_2 {}

impl Instruction for dload_2 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 2_u8;
        let val = frame.borrow().get_double_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValDouble {
            val
        })
    }


    fn to_string(&self) -> String {
        String::from("dload_2")
    }
}


pub struct dload_3 {}

impl Instruction for dload_3 {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let index = 3_u8;
        let val = frame.borrow().get_double_from_local_var(index);
        frame.borrow().operand_stack.borrow_mut().push(VMValEnum::VMValDouble {
            val
        })
    }

    fn to_string(&self) -> String {
        String::from("dload_3")
    }
}


pub struct caload {}

impl Instruction for caload {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        arr_load(frame, thread);
    }

    fn to_string(&self) -> String {
        String::from("caload")
    }
}


pub struct baload {}

impl Instruction for baload {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        arr_load(frame, thread);
    }


    fn to_string(&self) -> String {
        String::from("baload")
    }
}

pub struct saload {}

impl Instruction for saload {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        arr_load(frame, thread);
    }


    fn to_string(&self) -> String {
        String::from("saload")
    }
}


pub struct iaload {}

impl Instruction for iaload {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        arr_load(frame, thread);
    }


    fn to_string(&self) -> String {
        String::from("iaload")
    }
}


pub struct laload {}

impl Instruction for laload {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        arr_load(frame, thread);
    }


    fn to_string(&self) -> String {
        String::from("laload")
    }
}


pub struct faload {}

impl Instruction for faload {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        arr_load(frame, thread);
    }


    fn to_string(&self) -> String {
        String::from("faload")
    }
}


pub struct daload {}

impl Instruction for daload {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        arr_load(frame, thread);
    }


    fn to_string(&self) -> String {
        String::from("daload")
    }
}


pub struct aaload {}

impl Instruction for aaload {
    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        arr_load(frame, thread);
    }


    fn to_string(&self) -> String {
        String::from("aaload")
    }
}


fn arr_load(frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
    let ro_frame = frame.borrow();
    let mut rw_operand_stack = ro_frame.operand_stack.borrow_mut();
    let index_enum = rw_operand_stack.pop().unwrap();
    let arr_enum = rw_operand_stack.pop().unwrap();

    let index = VMValEnum::convert2integer(&index_enum).num;
    let arr_ref = get_val_from_vm_val_enum!(&arr_enum, VMValRef);
    let arr = VMHeap::get_vmobject(arr_ref.clone()).unwrap();

    assert!(arr.is_arr, "类型不匹配，此指令仅支持数组");
    if arr.arr_len <= index {
        panic!("{},{},{}", file!(), line!(), "数组越界");
    }

    rw_operand_stack.push(arr.slots.borrow_mut()[index as usize].clone());
}